# --*-- conding:utf-8 --*--
# @Time : 2025/6/19 18:58
# @Author : XU
# @Email : wsxzy98@outlook.com
# @File : stock_picking.py
# @Software : VScode

from odoo import models, fields, api, _
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_round, float_compare

class gs_stock_picking(models.Model):
    _inherit = 'stock.picking'

    customer_reference = fields.Char(string='客户参考号')

    totale_di_quantita = fields.Float(
        string='产品总数',
        compute='_compute_totale_di_quantita',
        store=True,
    )

    @api.depends('move_ids', 'move_ids.quantity')
    def _compute_totale_di_quantita(self):
        for picking in self:
            picking.totale_di_quantita = sum(move.quantity for move in picking.move_ids)

    def write(self, vals):
        res = super().write(vals)

        if 'x_studio_parcels' in vals:
            for picking in self:
                # 将x_studio_parcels的值赋给l10n_it_parcels（意大利本地化字段，需保证该字段已存在）
                picking.l10n_it_parcels = vals['x_studio_parcels']

        if 'customer_reference' in vals:
            for picking in self:
                picking.move_line_ids.write({'customer_reference': vals['customer_reference']})
        return res
    
    @api.model
    def create(self, vals):
        """
        重写create方法，创建出货单时也同步包裹数量到l10n_it_parcels
        """
        picking = super().create(vals)
        if 'x_studio_parcels' in vals:
            picking.l10n_it_parcels = vals['x_studio_parcels']
        return picking
    
    def action_put_in_pack(self, move_lines_to_pack=False):
        self.ensure_one()
        if self.state not in ('done', 'cancel'):
            #move_line_ids = self._package_move_lines(move_lines_to_pack=move_lines_to_pack)
            #res = self._pre_put_in_pack_hook(move_line_ids)
            #return res
            return self._pre_put_in_pack_hook(move_lines_to_pack or self.move_line_ids)
        raise UserError(_("There is nothing eligible to put in a pack."))

    
    def _pre_put_in_pack_hook(self, move_line_ids):
        return self._check_destinations(move_line_ids)

    def _check_destinations(self, move_line_ids):
        # 总是触发弹窗（不判断库位数量）
        view_id = self.env.ref('stock.stock_package_destination_form_view').id
        wiz = self.env['stock.package.destination'].create({
            'picking_id': self.id,
            'location_dest_id': move_line_ids[0].location_dest_id.id,
        })
        return {
            'name': _('Choose destination location'),
            'view_mode': 'form',
            'res_model': 'stock.package.destination',
            'view_id': view_id,
            'views': [(view_id, 'form')],
            'type': 'ir.actions.act_window',
            'res_id': wiz.id,
            'target': 'new',
            'context': {
                'move_lines_to_pack_ids': move_line_ids.ids,
            }
        }
    
    def action_load_inventory(self):
        """
        装载库存（仅出货单）：
        - 依据 picking.customer_reference + 源库位，从“散货” stock.quant 现查汇总出每个产品的可发数量。
        - 已有 move：不改 product_uom_qty（需求），只写 move.quantity。
          同产品多行 -> 按行顺序“分摊”；若还有余量，再回填到第一行（允许超量发货时才回填）。
        - 没有该产品的 move：新增一条“额外产品”（product_uom_qty=0），写 move.quantity。
        - 绝不把总量写得超过当前实时可用量（硬限额防护）。
        """
        Quant = self.env['stock.quant']
        Move = self.env['stock.move']
        Product = self.env['product.product']

        for picking in self:
            # 只允许交货单
            if picking.picking_type_code != 'outgoing':
                raise UserError(_('“装载库存”仅适用于交货单。'))
            if not picking.customer_reference:
                raise UserError(_('请先在拣货单上填写“客户参考号”。'))
            if not picking.location_id:
                raise UserError(_('请先设置源位置。'))

            # 草稿先确认
            if picking.state == 'draft':
                picking.action_confirm()

            # ========== 1) 现查散货 quant，按产品聚合 ==========
            quant_domain = [
                ('location_id', '=', picking.location_id.id),
                ('package_id', '=', False),                    # 只要散货
                ('quantity', '>', 0),
                ('customer_reference', '=', picking.customer_reference),
            ]
            quants = Quant.search(quant_domain)

            if not quants:
                raise UserError(_('未在源位置找到客户参考号为“%s”的散货库存。') % picking.customer_reference)

            avail_by_product = {}
            for q in quants:
                pid = q.product_id.id
                pr = q.product_id.uom_id.rounding or 0.0001
                # 按单位精度累加、避免浮点误差
                cur = avail_by_product.get(pid, 0.0)
                avail_by_product[pid] = float_round(cur + q.quantity, precision_rounding=pr)

            # ========== 2) 现有 move 按产品分组 ==========
            existing_moves = picking.move_ids_without_package.filtered(
                lambda m: m.state not in ('done', 'cancel')
            )
            moves_by_product = {}
            for mv in existing_moves:
                moves_by_product.setdefault(mv.product_id.id, self.env['stock.move'])
                moves_by_product[mv.product_id.id] |= mv

            # 是否允许超量发货：你之前选择的是“允许”（把多余回填到第一行）
            allow_overship = True

            # ========== 3) 逐产品分配 ==========
            for product_id, real_avail in avail_by_product.items():
                if float_compare(real_avail, 0.0,
                                 precision_rounding=Product.browse(product_id).uom_id.rounding or 0.0001) <= 0:
                    continue

                pr = Product.browse(product_id).uom_id.rounding or 0.0001

                # a) 有同产品的 move -> 顺序分摊
                if product_id in moves_by_product:
                    moves = (moves_by_product[product_id].sorted('sequence')
                             or moves_by_product[product_id].sorted('id'))

                    left = real_avail

                    for mv in moves:
                        if float_compare(left, 0.0, precision_rounding=pr) <= 0:
                            # 后续行清零，避免重复发货
                            if float_compare(mv.quantity, 0.0, precision_rounding=pr) != 0:
                                mv.write({'quantity': 0.0})
                            continue

                        need = mv.product_uom_qty or 0.0
                        take = min(left, need) if need > 0 else 0.0
                        # 直接覆盖写入
                        if float_compare(mv.quantity, take, precision_rounding=pr) != 0:
                            mv.write({'quantity': float_round(take, precision_rounding=pr)})
                        left = float_round(left - take, precision_rounding=pr)

                    # b) 还有余量：可选的超量回填到第一行
                    if allow_overship and float_compare(left, 0.0, precision_rounding=pr) > 0 and moves:
                        first = moves[:1]
                        new_qty = float_round(first.quantity + left, precision_rounding=pr)
                        first.write({'quantity': new_qty})
                        left = 0.0

                    # c) 安全网：总写入量不能超过“实时可用量”
                    written_total = sum(float_round(mv.quantity, precision_rounding=pr) for mv in moves)
                    if float_compare(written_total, real_avail, precision_rounding=pr) > 0:
                        # 多写出的部分回收，从最后一行往回减（不影响前面的分摊）
                        to_trim = float_round(written_total - real_avail, precision_rounding=pr)
                        for mv in reversed(moves):
                            if float_compare(to_trim, 0.0, precision_rounding=pr) <= 0:
                                break
                            cut = min(mv.quantity, to_trim)
                            if cut:
                                mv.write({'quantity': float_round(mv.quantity - cut, precision_rounding=pr)})
                                to_trim = float_round(to_trim - cut, precision_rounding=pr)

                else:
                    # c) 没有该产品 -> 新增额外产品行并写数量（同样做硬限额）
                    product = Product.browse(product_id)
                    new_move = Move.create({
                        'name': product.display_name or product.name,
                        'picking_id': picking.id,
                        'product_id': product_id,
                        'product_uom': product.uom_id.id,
                        'product_uom_qty': 0.0,                     # 需求不动
                        'location_id': picking.location_id.id,
                        'location_dest_id': picking.location_dest_id.id,
                        'company_id': picking.company_id.id,
                        'state': 'confirmed',
                        'additional': True,
                    })
                    new_move.write({'quantity': float_round(real_avail, precision_rounding=pr)})

            # 刷新拣货状态
            picking._compute_state()

        return True
    

    
